'''
Created on 17/02/2010

@author: eh01
'''
from xml.etree import ElementTree    
import re
import myxml

class Schema(object):
    
    def __init__(self):
        self.target_namespace = None
        self.elements = {}
        self.complex_types = {}
        self.simple_types = {}
        self.imported_namespaces = None
        self.imports = []

    def __str__(self):
        return "target_namespace:{0}, elements:{1}, complex_types:{2}, imported_namespace:{3}, imports:{4}".format(
             self.target_namespace, self.elements, self.complex_types, self.imported_namespaces, self.imports)
        
class ComplexType(object):
    
    def __init__(self):
        self.name = None
        self.elements = []
        self.simple_content = None
        self.attributes = {}
        
    def __str__(self):
        return "name:{0}, elements:{1}".format(self.name, self.elements)

class SimpleContent(object):
    
    def __init__(self):
        self.extension = None
        self.attributes = {}    
    
class SimpleType(object):
    
    def __init__(self):
        self.name = None
        self.restriction = None
        self.enums = {}

        
class Element(object):
    
    def __init__(self):
        self.name = None
        self.type = None
        self.min_occurs = 1
        self.max_occurs = 1

    def __str__(self):
        return "name:{0}, type:{1}".format(self.name, self.type)

class Attribute(object):
    
    def __init__(self):
        self.name = None
        self.type = None
        self.use = True
        self.default = None
                
class Type(object):
    
    def __init__(self):
        self.name = None
        self.namespace_prefix = None
        self.namespace = None

    def __str__(self):
        return "name:{0}, namespace_prefix:{1}, namespace:{2}".format(self.name, self.namespace_prefix, self.namespace)

class Import(object):
    
    def __init__(self):
        self.namespace = None
        self.schema_location = None
        
    def __str__(self):
        return "namespace:{0}, schema_location:{1}".format(self.namespace, self.schema_location)
    
def parse(schema_file_path):
    schema_file_as_string = open(schema_file_path, 'r')    
    root_element_tree = myxml.pre_parse_xml_to_add_namespace_data_to_elements(schema_file_as_string)
    schema_element = root_element_tree
    schema = Schema()
    schema.target_namespace = schema_element.attrib['targetNamespace']
    schema.imported_namespaces = schema_element.ns_map
    
    # parse imports
    for import_element in schema_element.findall('./{http://www.w3.org/2001/XMLSchema}import'):
        imp = Import()
        imp.namespace = import_element.attrib['namespace']
        imp.schema_location = import_element.attrib['schemaLocation']
        schema.imports.append(imp)
    
    # parse complex types
    for complex_type_element in schema_element.findall('./{http://www.w3.org/2001/XMLSchema}complexType'):
        complex_type = parse_complex_type_element(complex_type_element)
        schema.complex_types[complex_type.name] = complex_type
    
    # parse simple types
    for simple_type_element in schema_element.findall('./{http://www.w3.org/2001/XMLSchema}simpleType'):
        simple_type = parse_simple_type_element(simple_type_element)
        schema.simple_types[simple_type.name] = simple_type
    
    # parse elements
    for element_element in schema_element.findall('./{http://www.w3.org/2001/XMLSchema}element'):
        element = parse_element(element_element)
        schema.elements[element.name] = element
    return schema

def parse_complex_type_element(complex_type_element):
    complex_type = ComplexType()
    complex_type.name = complex_type_element.attrib['name']
    simple_content_element = complex_type_element.find('./{http://www.w3.org/2001/XMLSchema}simpleContent')
    if simple_content_element:
        # most likely a complex type with simple content (e.g. no elements)
        complex_type.simple_content = parse_simple_content_element(simple_content_element)
    else:
        sequence_element = complex_type_element.find('./{http://www.w3.org/2001/XMLSchema}sequence')
        # most likely a complex type with complex content (e.g. with elements)
        for element_or_choice_element in sequence_element.getchildren():
            if element_or_choice_element.tag == '{http://www.w3.org/2001/XMLSchema}choice':
                choices = {}
                choice_element = element_or_choice_element
                for element_element in choice_element.findall('./{http://www.w3.org/2001/XMLSchema}element'):
                    element = parse_element(element_element)
                    choices[element.name] = element
                complex_type.elements.append(choices)
            else:
                element = parse_element(element_or_choice_element)
                complex_type.elements.append(element)                
    for attribute_element in complex_type_element.findall('./{http://www.w3.org/2001/XMLSchema}attribute'):
        attribute = parse_attribute(attribute_element)
        complex_type.attributes[attribute.name] = attribute
    return complex_type

def parse_simple_type_element(simple_type_element):
    simple_type = SimpleType()
    simple_type.name = simple_type_element.attrib['name']
    restriction_element = simple_type_element.find('./{http://www.w3.org/2001/XMLSchema}restriction')
    simple_type.restriction = create_type(restriction_element.attrib['base'], restriction_element)
    for enumeration_element in restriction_element.findall('./{http://www.w3.org/2001/XMLSchema}enumeration'):
        value = enumeration_element.attrib['value']
        simple_type.enums[value] = value
    return simple_type

def parse_simple_content_element(simple_content_element):
    simple_content = SimpleContent()
    extension_element = simple_content_element.find('./{http://www.w3.org/2001/XMLSchema}extension')
    simple_content.extension = create_type(extension_element.attrib['base'], extension_element)
    for attribute_element in extension_element.findall('./{http://www.w3.org/2001/XMLSchema}attribute'):
        attribute = parse_attribute(attribute_element)
        simple_content.attributes[attribute.name] = attribute
    return simple_content

def parse_attribute(attribute_element):
    attribute = Attribute()
    attribute.name = attribute_element.attrib['name']
    attribute.type = create_type(attribute_element.attrib['type'], attribute_element)
    if attribute.type.name != 'string':
        print attribute.type.name
    if attribute_element.attrib['use'] == 'optional':
        attribute.use = False
    if 'default' in attribute_element.attrib:
        attribute.default = attribute_element.attrib['default']
    return attribute
    
def parse_element(element_element):
    # type_string form is "nsPrefix:typeName"
    type_string_pattern = re.compile(r'(\w*):(\w*)')    
    element = Element()
    element.name = element_element.attrib['name']
    if 'minOccurs' in element_element.attrib:
        element.min_occurs = int(element_element.attrib['minOccurs'])
    if 'maxOccurs' in element_element.attrib:
        max_occurs = element_element.attrib['maxOccurs']
        if max_occurs != 'unbounded':
            element.max_occurs = int(element_element.attrib['maxOccurs'])
        else:
            element.max_occurs = -1        
    type_string = element_element.attrib['type']
    matches = type_string_pattern.match(type_string)
    type = Type()
    type.name = matches.group(2)
    type.namespace_prefix = matches.group(1)
    type.namespace = element_element.ns_map[type.namespace_prefix]
    element.type = type
    return element

def create_type(qname, referencing_element):
    (namespace_prefix, seperator, name) = qname.partition(':')
    type = Type()
    type.name = name
    type.namespace_prefix = namespace_prefix
    type.namespace = referencing_element.ns_map[namespace_prefix]
    return type

if __name__ == '__main__':
    type_string_pattern = re.compile(r'(\w*):(\w*)')
    matches = type_string_pattern.match('blah:yaya')
    print matches.group(0)
    print matches.group(1)
    print matches.group(2)